知识点:
自定义按钮:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Button {
id:btn
x: 0
y: 62
text: "Quit2"
style:ButtonStyle {
background: Rectangle {
implicitWidth: 100
implicitHeight: 25
border.width: btn.pressed ? 2 : 1
}
}
}
}
main.qml
多个窗口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 1.4
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Button { // 会默认放到 data 列表中
id:btn
text: "Quit"
}
Window {
id: root2
visible: true
title: qsTr("Hello World")
width: 300
height: 100
flags: Qt.Popup
Text{
text: "Hello Qt Quick"
}
}
}
main.qml
颜色 LinearGradient :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.12
import QtQuick.Window 2.12
import QtGraphicalEffects 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
width: 500
height: 500
border.color: Qt.lighter("purple")
border.width: 2
radius: 15
LinearGradient {
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(500, 500)
gradient: Gradient {
GradientStop {
position: 0.000
color: Qt.rgba(1, 0, 0, 1)
}
GradientStop {
position: 0.167
color: Qt.rgba(1, 1, 0, 1)
}
GradientStop {
position: 0.333
color: Qt.rgba(0, 1, 0, 1)
}
GradientStop {
position: 0.500
color: Qt.rgba(0, 1, 1, 1)
}
GradientStop {
position: 0.667
color: Qt.rgba(0, 0, 1, 1)
}
GradientStop {
position: 0.833
color: Qt.rgba(1, 0, 1, 1)
}
GradientStop {
position: 1.000
color: Qt.rgba(1, 0, 0, 1)
}
}
source: Image { source:"images/ufo.png" }
}
Component.onCompleted: {
console.log(color.r,color.g,color.b,color.a);
}
}
}
main.qml
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200714143603000-1514422266.png)
Text 控件 多余文字显示 三个点:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.12
import QtQuick.Window 2.12
import QtGraphicalEffects 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
color: "cyan"
Rectangle{
id:rect1
color: "purple"
width: 150
height: 100
Text {
id: txt1
anchors.fill:parent
text: qsTr("Hello Red Text,Hello Red Text,Hello Red Text,Hello Red Tex")
color:"red"
clip: true
wrapMode: Text.WrapAnywhere
elide: Text.ElideRight
}
}
}
View Code
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200714214511797-753308670.png)
Image 相关:
显示GIF 图片:
Image不能显示GIF,因为Image只能显示静态图片,可以使用 AnimatedImage 显示动态图片
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
color: "cyan"
Image {
id: img1
width: 100
height: 100
source: "images/1.gif" // 不能显示GIF,因为Image只能显示静态图片
}
// 可以使用 AnimatedImage 显示动态图片
AnimatedImage {
id:aniImg1
x:100
y:100
width: 100
height: 100
source: "images/1.gif"
}
}
View Code
异步加载网络图片:
使用了 BusyIndicator 组件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
color: "cyan"
Rectangle{
width: 500
height: 500
border.color: "red"
border.width: 2
BusyIndicator{
id:loading
running: true
anchors.centerIn: parent
z:2 // z-index
}
Text {
id: desc
visible: false
anchors.centerIn: parent
z:3 // z-index
}
Image {
id: img1
cache: false // 在加载大型图片一般不要进行缓存
anchors.fill: parent
source: "http://pic1.juimg.com/161122/330846-16112222051832-lp.jpg"
fillMode: Image.PreserveAspectFit
asynchronous: true // 开启异步加载 图片 网络资源自动就是异步,本地资源默认都是同步
onStatusChanged: {
if(img1.status === Image.Loading){
loading.running = true;
desc.visible = false;
// console.log("loading");
}else if(img1.status === Image.Ready){
loading.running = false;
// console.log("ready");
}else if(img1.status === Image.Error){
loading.running = true;
desc.visible = true;
desc.text = "load img error";
// console.log("error");
}
}
// focus: true
// Keys.onPressed: {
// if(event.key === Qt.Key_A){
// console.log(img1.status,"|||| ",Image.Error);
// }
// }
}
}
}
View Code
效果:
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200714231947846-817278101.gif)
图片查看器(一次打开单张图片):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
minimumWidth: 480
minimumHeight: 360
BusyIndicator{
id:loading
running: false
anchors.centerIn: parent
z:2
}
Text {
id: stateTxt
visible: false
anchors.centerIn: parent
z:3
}
Image {
id: imageViewer
asynchronous: true // 异步加载
cache: false // 关闭缓存
anchors.fill: parent
fillMode: Image.PreserveAspectFit
onStatusChanged: {
if(imageViewer.status === Image.Loading){
loading.running = true;
stateTxt.visible = false;
}else if(imageViewer.status === Image.Ready){
loading.running = false;
}else if(imageViewer.status === Image.Error){
loading.running = true;
stateTxt.visible = true;
stateTxt.text = "load image error";
}
}
}
Button{
id:openFile
text: "Open"
anchors.left: parent.left
anchors.leftMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
onClicked:fileDialog.open()
}
FileDialog{
id:fileDialog
title: "Please choose a file"
nameFilters: ["Image Files (*.jpg *.png *.gif)"]
onAccepted: {
imageViewer.source = fileDialog.fileUrl;
var imageFile =new String(fileDialog.fileUrl);
imagePath.text = imageFile.slice(8);
}
}
Text {
id: imagePath
anchors.left: openFile.right
anchors.leftMargin: 8
anchors.verticalCenter: openFile.verticalCenter
font.pixelSize: 18
}
}
main.qml
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200716104503556-1753270125.png)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
minimumWidth: 480
minimumHeight: 360
BusyIndicator{
id:loading
running: false
anchors.centerIn: parent
z:2
}
Text {
id: stateTxt
visible: false
anchors.centerIn: parent
z:3
}
Image {
id: imageViewer
asynchronous: true // 异步加载
cache: false // 关闭缓存
anchors.fill: parent
fillMode: Image.PreserveAspectFit
onStatusChanged: {
if(imageViewer.status === Image.Loading){
loading.running = true;
stateTxt.visible = false;
}else if(imageViewer.status === Image.Ready){
loading.running = false;
}else if(imageViewer.status === Image.Error){
loading.running = true;
stateTxt.visible = true;
stateTxt.text = "load image error";
}
}
}
Button{
id:openFile
text: "Open"
anchors.left: parent.left
anchors.leftMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
onClicked:fileDialog.open()
}
FileDialog{
id:fileDialog
title: "Please choose a file"
nameFilters: ["Image Files (*.jpg *.png *.gif)","Bitmap Files (*.bmp)","* (*.*)"]
selectedNameFilter:"Image Files (*.jpg *.png *.gif)" // 默认过滤器
selectMultiple: true // 选择多个文件
onAccepted: {
imageViewer.source = fileDialog.fileUrls[0];
var imageFile =new String(fileDialog.fileUrl[0]);
imagePath.text = imageFile.slice(8);
}
}
Text {
id: imagePath
anchors.left: openFile.right
anchors.leftMargin: 8
anchors.verticalCenter: openFile.verticalCenter
font.pixelSize: 18
}
}
一次可以选择多个文件
Loader:
Loader 用来动态加载QML Component 组件,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Loader{
id:redLoader
anchors.left: parent.left
anchors.leftMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
sourceComponent: colorComponent
onLoaded: {
item.color = "red"; // 这里的item 指的是 所加载的顶层对象即 rect
}
}
Component{
id:colorComponent // 注Component 中只能有一个id 属性 和 一个顶层对象!!!
Rectangle{
id:rect
width: 50
height: 50
signal zcb(color clr)
MouseArea{
anchors.fill: parent
onPressed: rect.zcb(rect.color)
}
onZcb:{
rect.color = "yellow";
}
}
}
}
View Code
如果Loader加载的Item 想要处理按键事件,必须要把Loader 中的 focus 设置为true
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Loader{
id:redLoader
anchors.left: parent.left
anchors.leftMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
sourceComponent: colorComponent
onLoaded: {
item.color = "red";
}
// 如果Loader加载的Item 想要处理按键事件,必须要把Loader focus 设置为true,这样它所加载的组件才可以接收到focus
focus: true // 开启按键事件
}
Component{
id:colorComponent
Rectangle{
id:rect
width: 50
height: 50
signal zcb(color clr)
MouseArea{
anchors.fill: parent
onPressed: rect.zcb(rect.color)
}
onZcb:{
rect.color = "yellow";
}
focus: true
Keys.onPressed: {
console.log(event.key);
event.accepted = true;
}
}
}
}
View Code
从文件中加载组件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Loader{
id:redLoader
anchors.left: parent.left
anchors.leftMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4
// sourceComponent: colorComponent
source: "MyTest.qml"
onLoaded: {
console.log(item); //此时 item 仍然是顶层对象
item.color = "red";
}
// 如果Loader加载的Item 想要处理按键事件,必须要把Loader focus 设置为true,这样它所加载的组件才可以接收到focus
focus: true // 开启按键事件
}
}
main.qml
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
Rectangle{
id:colorComponent
width: 50
height: 50
signal zcb(color clr)
MouseArea{
anchors.fill: parent
onPressed: colorComponent.zcb(colorComponent.color)
}
onZcb:{
colorComponent.color = "yellow";
}
focus:true
Keys.onPressed: {
console.log(event.key);
event.accepted = true;
}
}
MyTest.qml
动态创建和销毁控件:
利用Loader,可以通过设置source为 空串,设置sourceComponent 为undefined 可以销毁控件 ~
在js 中动态创建组件对象:
有两种方式:
1,使用Qt.createComponent() 动态创建一个组件对象,然后使用Component 的createObject() 创建它的实例对象。
2,使用Qt.createQmlObject() 从一个QML字符串直接创建一个实例对象
注:
如果已经有一个QML文件中定义了一个组件,你想创建它的实例对象,这时使用1较好。
如果QML对象本身是在运行时产生的,那么Qt.createQmlObject() 可能是比较好的选择。
第一种,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
anchors.fill: parent
focus: true
Keys.onPressed: {
if(event.key === Qt.Key_A){
// 创建组件对象
var newCom = Qt.createComponent("MyTest.qml");
newCom.createObject(rect,{"color":"cyan"});// 第一个参数为父级,后面是新创建组件的属性
}
}
}
}
main.qml
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
Rectangle{
id:colorComponent
width: 50
height: 50
signal zcb(color clr)
MouseArea{
anchors.fill: parent
onPressed: colorComponent.zcb(colorComponent.color)
}
onZcb:{
colorComponent.color = "yellow";
}
focus:true
Keys.onPressed: {
console.log(event.key);
event.accepted = true;
}
}
MyTest.qml
第二种,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
anchors.fill: parent
focus: true
Keys.onPressed: {
if(event.key === Qt.Key_A){
// 创建组件对象
var newCom = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20;}',
rect,
"newCom01"); // 第三个参数 是给对象关联一个文件路径,主要用于报告错误
}
}
}
}
main.qml
销毁动态创建的对象:
这里说的销毁不是仅仅把对象的visible 设为false或者是opacity 设置为0 。
需要调用 destroy() 函数,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.10
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
anchors.fill: parent
property var mycom: null
focus: true
Keys.onPressed: {
if(event.key === Qt.Key_A){
// 创建组件对象
rect.mycom = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20;}',
rect,
"newCom01"); // 第三个参数 是给对象关联一个文件路径,主要用于报告错误
}
if(event.key === Qt.Key_B){
console.log(rect.mycom);
rect.mycom.destroy(0); // 0s 后销毁
}
}
}
}
main.qml
点击a 创建,点击b 销毁~
Qt Quick 元素布局:
三大类:
1,前面说过的 锚布局(Anchor),它是利用Item 的anchors 属性实现,非常方便!!!
2,定位器(Position),它包含了Row(行定位器),Column(列定位器),Grid(表格定位器),Flow(流定位器)
3,布局管理器(Layout),它包含 行布局(RowLayout),列布局(ColumnLayout),表格布局(GridLayout)
定位器Position
略
布局管理器Layout
Qml 的布局管理器和 Qt widgets 相似,它与定位器的不同之处在于:布局管理器会自动调整子Item 的尺寸来适应界面大小的变化。
要使用布局管理器,需要引入Layouts 模块。
GridLayout:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rootItem
width: 360
height: 240
color:"#eee"
GridLayout{
width: 200
anchors.left: parent.left
anchors.leftMargin: 4
anchors.top: parent.top
anchors.topMargin: 4
rows:2
columns: 2
rowSpacing: 4
columnSpacing: 4
Rectangle{
id:rect1
width:100
height: 100
color: "purple"
Layout.columnSpan: 2
Layout.rowSpan: 2
Layout.fillWidth: true
}
Rectangle{
id:rect2
width:100
height: 100
color: "cyan"
}
Rectangle{
id:rect3
width:100
height: 100
color: "yellow"
}
}
}
}
main.qml
RowLayout
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rootItem
width: 400
height: 240
color:"#eee"
RowLayout{
width: 400
anchors.left: parent.left
anchors.leftMargin: 4
anchors.top: parent.top
anchors.topMargin: 4
Rectangle{
id:rect1
width:100
height: 100
color: "purple"
}
Rectangle{
id:rect2
width:100
height: 100
color: "cyan"
}
Rectangle{
id:rect3
width:100
height: 100
color: "yellow"
Layout.fillWidth: true
}
}
}
}
main.qml
ColumnLayout:
类似...
QML 常用控件:
1,行编辑:
TextInput ,TextField
2,文本块:
TextEdit,TextArea
3,互斥分组:
ExclusiveGroup
4,RadioButton:
单选按钮
5,CheckBox:
多选框
6,GroupBox:
分组框
7,ComboBox:
下拉框
8,ProgressBar:
进度条
9,TabView:
点击标签会进入对应界面,选项卡控件
10,Slider:
滑块控件
11,Flickable:
Flickable提供一个较小的视窗来显示一个较大的内容给用户,并且用户可以对改内容进行拖拽和轻拂
12,Screen:
它是显示Item 的那个屏幕,它提供了一些只读属性来描述屏幕参数。
Canvas 画布:
Canvas 是Item 的派生类,
1,画布:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Canvas{
width: 300
height: 200
}
View Code
这即是一块画布,
2,画师:
画师是Context2D ,QT帮助文档直接搜索Context2D ,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Canvas{
width: 300
height: 200
onPaint: {
var ctx = getContext("2d"); // 在QML 中,一块画布只有一个画师
}
}
View Code
3,画笔:关于画笔的属性设置,在Contex2D对象 上都有,有笔的粗细(lineWidth),笔的颜色(strokeStyle)之类的
4,画刷:
画刷是用来填充画笔勾勒出的区域的,属性是fillStyle,
坐标系:
在2D世界,原点位于屏幕左上角(0,0), 向右是x 轴,向下是y轴。
图元:
基本的图元有线,弧,矩形,曲线,文本,图片,
小试牛刀:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
onPaint: {
var ctx = getContext("2d"); // 在QML 中,一块画布只有一个画师
ctx.lineWidth = 2;
ctx.strokeStyle = "red"; // 画笔颜色
ctx.fillStyle = "blue"; // 画刷 颜色
ctx.beginPath();
ctx.rect(60,50,120,80);
ctx.fill();
ctx.stroke(); // 使用stroke() 结束路径的绘制
var gradient = ctx.createRadialGradient(200,140,40,280,220,20);
gradient.addColorStop(0.0,Qt.rgba(1,0,0,1.0));
gradient.addColorStop(1.0,Qt.rgba(0,0,1,1.0));
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.rect(200,140,80,80);
ctx.fill();
ctx.stroke();
}
}
}
main.qml
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
onPaint: {
var ctx = getContext("2d"); // 在QML 中,一块画布只有一个画师
ctx.lineWidth = 2;
ctx.strokeStyle = "red"; // 画笔颜色
ctx.fillStyle = "blue"; // 画刷 颜色
ctx.beginPath();
ctx.moveTo(100,80); // 移动到 100,80
ctx.lineTo(100,200); // 从当前位置到(x,y)点绘制一条直线
ctx.lineTo(300,200);
ctx.closePath(); // 结束当前的路径,从路径终点到起点绘制一条直线来封闭路径
ctx.fill();
ctx.stroke(); // 使用stroke() 结束路径的绘制
}
}
}
main.qml
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
onPaint: {
var ctx = getContext("2d"); // 在QML 中,一块画布只有一个画师
ctx.lineWidth = 2;
ctx.strokeStyle = "red"; // 画笔颜色
ctx.fillStyle = "blue"; // 画刷 颜色
ctx.font = "42px sans-serif";
ctx.beginPath();
ctx.moveTo(4,4);
ctx.bezierCurveTo(0,height - 1,width -1 ,height/2,width/4,height/4);
ctx.lineTo(width/2,height/4);
ctx.arc(width*5/8,height/4,width/8,Math.PI,0,false);
ctx.ellipse(width*11/16,height/4,width/8,height/4);
ctx.lineTo(width/2,height*7/8);
ctx.text("Hello World",width/4,height*7/8);
ctx.fill();
ctx.stroke(); // 使用stroke() 结束路径的绘制
}
}
}
View Code
与文本相关:
有三个方法:fillText() ,strokeText(),text()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
onPaint: {
var ctx = getContext("2d"); // 在QML 中,一块画布只有一个画师
ctx.lineWidth = 2;
ctx.strokeStyle = "red"; // 画笔颜色
ctx.font = "42px sans-serif";
ctx.fillStyle = "green"; // 画刷颜色
//1
ctx.beginPath();
ctx.text("Hello World -fill",50,50);
ctx.fill();
//2
ctx.fillText("Hello World -fillText",50,150);
//3
ctx.beginPath();
ctx.text("Hello World -stroke",50,200);
ctx.stroke();
//4
ctx.strokeText("Hello World -strokeText",50,250);
//5
ctx.beginPath();
ctx.text("Hello World -fill -stroke",50,300);
ctx.fill();
ctx.stroke();
}
}
}
View Code
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200718135118998-2143226025.png)
绘制图片:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
property var imgSrc:"images/rocket.png"
onPaint: {
var ctx = getContext("2d");
//1
ctx.drawImage(imgSrc,100,100);
}
Component.onCompleted: loadImage(imgSrc); // loadImage 会异步加载图片,当图片加载完成时会发射imageLoaded 信号
onImageLoaded: {
requestPaint(); // 重新绘制 Canvas 画布
}
}
}
View Code
变换:
平移(translate),旋转(rotate),缩放(scale),错切(shear),
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
property var imgSrc:"images/rocket.png"
onPaint: {
var ctx = getContext("2d");
ctx.lineWidth = 2;
ctx.strokeStyle = "blue";
ctx.fillStyle = "purple";
ctx.save();
ctx.beginPath();
ctx.translate(width/2,height/2);
ctx.arc(0,0,30,0,Math.PI*2);
ctx.arc(0,0,50,0,Math.PI*2);
ctx.arc(0,0,70,0,Math.PI*2);
ctx.arc(0,0,90,0,Math.PI*2);
ctx.stroke();
ctx.restore(); // 恢复到save() 之前的状态
ctx.save();
ctx.translate(width/2,30);
ctx.font = "42px sans-serif";
ctx.textAlign="center";
ctx.fillText("concentric circles",0,0);
ctx.restore();
}
}
}
View Code
Note :restore() 和 save()结合使用,
裁剪:
Context2D 的clip() 方法,让我们能够根据当前路径包围的区域来裁剪。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{
width: 800
height: 600
property var imgSrc:"images/1.jpg"
onPaint: {
var ctx = getContext("2d");
ctx.lineWidth = 2;
ctx.strokeStyle = "blue";
ctx.fillStyle = Qt.rgba(0.3,0.5,0.7,0.3);
ctx.save();
ctx.beginPath();
ctx.arc(180,150,80,0,Math.PI*2,true);
ctx.closePath();
ctx.clip(); // 裁剪
ctx.drawImage(imgSrc,0,0);
ctx.stroke();
ctx.fill();
ctx.rotate(Math.PI/5);
ctx.font = "italic bold 32px serif";
ctx.fillStyle = "red"; // 字体填充颜色
ctx.fillText("Hello World,Wosdfjklf ",100,70);
ctx.restore();
}
Component.onCompleted: loadImage(imgSrc);
onImageLoaded: requestPaint();
}
}
View Code
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200718143124374-657331394.png)
图像合成:
globalCompositeOperation
![](https://img2020.cnblogs.com/blog/1600711/202007/1600711-20200718144402039-101773201.png)
QML 动画:
基本动画对象:
PropertyAnimation: 通过改变各种类型的property 来产生动画
NumberAnimation: PropertyAnimation 的子类,专门改变数字类型的property来产生动画,效率比PropertyAnimation 好,
ColorAnimaiton : PropertyAnimation 的子类,专门改变color 类型的property 来产生动画,效率比PropertyAnimation 好,
RotationAnimaiton : PropertyAnimation 的子类,专门改变rotation 值来产生动画,效率比PropertyAnimation 好,另外还提供旋转方向等附加属性,
Vector3dAnimation: PropertyAnimation 的子类,当一个Vector3d值 发生变化时使用。
PathAnimation: 让对象沿一个给定的方向移动
SmoothedAnimation: 允许一个property 跟踪一个值,产生平滑动画
SpringAnimation, 允许一个property 跟踪一个值,动画效果类似于弹簧运动,
分组动画对象:
SequentialAnimation: 顺序执行一系列动画
ParallAnimation : 并行执行一系列动画
动画搭档:
State,它是Item的状态,不同状态对应不同的界面效果和业务逻辑。
Transition, 过渡,它用来衔接不同状态,使状态变化过程平滑。
协同动画元素:
Behavior : 它为Item 的property 变化绑定一个默认的动画对象。
ParentAnimation, 当改变一个Item的parent的时候使用,使得从旧parent到新parent的过程更平滑,通常与State,Transition,ParentChange联合使用。
AnchorAnimation: 当改变一个Item 的 anchor的时候使用,通常与State,Transition,AnchorChange联合使用。
PauseAnimation:如果在动画过程插入它,可以将动画过程暂停一段时间。
PropertyAction: 动画执行过程立即改变某个属性。
ScriptAction:动画执行过程运行一段JS脚本。
测试代码:
PropertyAnimation定义的三种方式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 50
anchors.centerIn: parent
color: "blue"
MouseArea{
anchors.fill: parent
onClicked: animation.running = true;
}
}
Rectangle{
id:rect2
width: 50
height: 50
color: "red"
}
PropertyAnimation{
id:animation
targets: [rect,rect2]
properties: "width,height"
to:150
duration: 1000;
}
}
View Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 50
anchors.centerIn: parent
color: "blue"
MouseArea{
anchors.fill: parent
onClicked: PropertyAnimation{ // 在信号处理器中直接使用动画
id:animation
target: rect
properties: "width,height"
to:150
duration: 1000;
}
}
}
}
在信号处理器中直接使用动画
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 50
anchors.centerIn: parent
color: "blue"
MouseArea{
id:mouseArea
anchors.fill: parent
}
PropertyAnimation on width{ // Animation on 这种语法格式 可以将一个动画和一个属性关联起来
to:150
duration: 1000
running: {
return mouseArea.pressed
}
}
}
}
Animation on 语法格式
Animation 的started() 和 stopped() 信号:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
property var animation;
PropertyAnimation{
id:toSquare
target: rect
property: "width"
to:150
duration: 1000
onStarted: {
rect.animation = toSquare;
rect.color = "red";
}
onStopped: {
rect.color = "blue";
}
}
PropertyAnimation{
id:toRect
target: rect
property: "width"
to:50
duration: 1000
onStarted: {
rect.animation = toRect;
rect.color = "red";
}
onStopped: {
rect.color = "blue";
}
}
MouseArea{
id:mouseArea
anchors.fill: parent
onClicked: {
if(rect.animation == toRect || rect.animation == undefined){
toSquare.start();
}else{
toRect.start();
}
}
}
}
}
View Code
NumberAnimation:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
NumberAnimation {
id:numAni
target: rect
property: "width"
to:150
duration: 200
easing.type: Easing.InOutQuad
}
MouseArea{
id:mouseArea
anchors.fill: parent
onClicked: {
numAni.running = true;
}
}
}
}
View Code
ColorAnimation:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
MouseArea{
id:mouseArea
anchors.fill: parent
onClicked: ColorAnimation {
target: rect
property: "color"
to: "green"
duration: 200
}
}
}
}
View Code
RotationAnimation:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 50
height: 150
anchors.centerIn: parent
color: "blue"
// transformOrigin: Item.TopRight
Text {
id: txt
anchors.fill: parent
text: qsTr("text")
}
MouseArea{
id:mouseArea
anchors.fill: parent
onClicked: RotationAnimation{
target: rect
to:90
duration: 1000
direction: RotationAnimation.Clockwise // 顺时针
}
}
}
}
View Code
PathAnimation:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Canvas{ // 画一个半圆
width: 400
height: 240
onPaint: {
var ctx = getContext("2d");
ctx.lineWidth = 4;
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.arc(200,0,160,Math.PI*2,0,false);
ctx.stroke();
}
}
Rectangle{
id:rect
width: 40
height: 40
color: "blue"
x:20
y:0
MouseArea{
id:mouseArea
anchors.fill:parent
onClicked: pathAnim.start();
}
PathAnimation{
id:pathAnim
target: rect
duration: 6000
anchorPoint: "20,20"// rect 的center
orientation: PathAnimation.BottomFirst
orientationEntryDuration: 200 // 调整初始姿态的时间
endRotation: 0 // 最后的角度 :0
orientationExitDuration: 200 // 调整终止时姿态的时间
easing.type: Easing.InOutCubic
path: Path{
startX: 40
startY: 0
PathArc{
x:360 // 终点(360,0)
y:0
useLargeArc: true; // 采用 优弧(较大)模式
radiusX: 160 // 椭圆的两个半轴
radiusY: 160
direction: PathArc.Counterclockwise // 逆时针
}
}
}
}
}
View Code
SmoothedAnimation:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 40
height: 40
color: "red"
x:20
y:20
SmoothedAnimation{ // SmoothedAnimation默认采用 easing.type 为InOutQuad
id:smoothAni1
target: rect
property: "x"
duration: 1000
velocity: -1 //速率 默认速率是200units/秒, -1 为禁用速率
// 如果duration和 velocity同时设置,那么会使用速率计算出一个时间, 它和duration的较短者会被采用。
}
SmoothedAnimation{
id:smoothAni2
target: rect
property: "y"
velocity: 100
}
}
MouseArea{
anchors.fill: parent
onClicked: {
smoothAni1.from = rect.x;
smoothAni1.to = mouse.x + 4;
smoothAni1.start();
smoothAni2.from = rect.y;
smoothAni2.to = mouse.y + 4;
smoothAni2.start();
}
}
}
View Code
SpringAnimation:
可以用来模拟弹簧的震荡行为,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 40
height: 40
color: "red"
x:20
y:20
}
SpringAnimation{
id:springAni1
target: rect
property: "x"
spring: 3 // 加速度 0-5 有意义 默认为0
damping: 0.06 //衰减系数0-1.0 有意义, 值越大会越快平复
epsilon: 0.25 //允许设定一个最接近0 的阈值代表0 ,如果是基于像素动画,建议0.25 ,如果是基于scale动画,建议0.005 。 默认为0.01 ,调整epsilon 可能会有一定的性能提升
}
SpringAnimation{
id:springAni2
target: rect
property: "y"
spring: 3
damping: 0.06
epsilon: 0.25
}
MouseArea{
anchors.fill: parent
onClicked: {
springAni1.from = rect.x;
springAni1.to = mouse.x - 20;
springAni1.start();
springAni2.from = rect.y;
springAni2.to = mouse.y - 20;
springAni2.start();
}
}
}
View Code
组合动画:
略
State:
在QML中,状态是定义在State类型中的一系列属性配置。不同的配置可能有不同的作用:
1,显示一些UI组件,隐藏另一些
2,想用户呈现不同的操作和功能
3,启动,暂停,停止动画
4,在某种新的状态下执行某些脚本
5,改变某个特定Item的property的值
6,显示一个不同的view或screen
等等...
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 360
height: 240
color: "#eee"
Text {
id: centerTxt
text: qsTr("Just a text!")
anchors.centerIn: parent
font.pixelSize: 24
MouseArea{
id:mouseArea
anchors.fill: parent
onPressed: {
centerTxt.state = "blueText";
}
onReleased: {
centerTxt.state = "redText";
}
}
state:"redText"
states: [
State {
name: "redText"
changes: [
PropertyChanges {
target: centerTxt
color:"red"
}
]
},
State {
name: "blueText"
PropertyChanges {
target: centerTxt
color:"blue"
}
}
]
}
}
}
使用State 变换文本
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Layouts 1.12
Window {
id: root
visible: true
title: qsTr("Hello World")
width: 800
height: 600
Rectangle{
id:rect
width: 360
height: 240
color: "#eee"
Text {
id: centerTxt
text: qsTr("Just a text!")
anchors.centerIn: parent
font.pixelSize: 24
MouseArea{
id:mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
}
state:"redText"
states: [
State {
name: "redText"
when:mouseArea.pressedButtons === Qt.RightButton
changes: [
PropertyChanges {
target: centerTxt
color:"red"
},
PropertyChanges {
target: centerTxt
font.bold:true
font.italic: true
}
]
},
State {
name: "blueText"
when: mouseArea.pressed
PropertyChanges {
target: centerTxt
color:"blue"
font.pixelSize: 48;
}
}
]
}
}
}
点击左右鼠标键,产生不同效果
C++ 与 QML 混合编程:
在QML中使用c++ 类和对象:
要想将一个类或对象导出到QML,前提条件是:
1,从QObject 或 QObject 的派生类继承
2,使用Q_OBJECT 宏
省略...
在QML中使用c++ 类和对象:
|